import java.net.Socket;
import java.net.UnknownHostException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;


public class Client implements Functionality{

    private String host;
    private int port;
    private Socket sock;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private Request request;



    public Client(String host, int port) {
        this.host = host;
        this.port = port;
        try {
            this.sock = new Socket(host,port);
            this.out = new ObjectOutputStream(sock.getOutputStream());
            //out.flush();
            this.in = new ObjectInputStream(sock.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public byte[] readData(int key) throws IOException, ClassNotFoundException {
        this.sock = new Socket(host,port);
        this.out = new ObjectOutputStream(sock.getOutputStream());
        //out.flush();
        this.in = new ObjectInputStream(sock.getInputStream());
        Request result = null;
        this.request = new Request(key,new byte[1],"get");
        out.writeObject(request);
        Object read = in.readObject();
        Request[] requests=null;
        if(read instanceof Request[]){
            requests=(Request[])read;
            System.out.println("reizinho "+requests.length);
            System.out.println("escaxe8");
            result=esperaRespostas(requests);
            System.out.println("escaxe9");
        }
        in.close();
        out.close();
        return result.getData().get(key);
    }

    public byte[][] readData(int[] keys) throws IOException, ClassNotFoundException {
        this.sock = new Socket(host,port);
        this.out = new ObjectOutputStream(sock.getOutputStream());
        //out.flush();
        this.in = new ObjectInputStream(sock.getInputStream());
        System.out.println("Obteve Resposta");
        Request result = null;
        byte[][] ans=null;
        this.request = new Request(keys,new byte[keys.length][],"get");
        out.writeObject(request);
        Object read = in.readObject();
        Request[] requests=null;
        if(read instanceof Request[]){
            requests=(Request[])read;
            result=esperaRespostas(requests);
        }
        for(int i=0; i<keys.length;i++){
            ans[i]= result.getData().get(keys[i]);
        }
        in.close();
        out.close();
        return ans;
    }

    public void writeData(int key, byte[] data) throws IOException, ClassNotFoundException {
        this.sock = new Socket(host,port);
        this.out = new ObjectOutputStream(sock.getOutputStream());
        //out.flush();
        this.in = new ObjectInputStream(sock.getInputStream());
        this.request = new Request(key,data,"put");
        System.out.println("Envia CentralServer");
        out.flush();
        out.writeObject(request);
        System.out.println("Enviou CentralServer");

        Object lido = in.readObject();
        System.out.println("Recebeu do CentralServer");

        if(lido instanceof Request[]){
            System.out.println("e um Request[]");
            Request[] resp = (Request[])lido;
            for(Request r : resp){
                Socket sock = new Socket(r.getAddress().getHost(),r.getAddress().getPort());
                System.out.println("Host: "+r.getAddress().getHost()+ " Port: "+r.getAddress().getPort());
                System.out.println("before in e out");
                ObjectOutputStream out= new ObjectOutputStream(sock.getOutputStream());
                System.out.println("before in");
                //ObjectInputStream in = new ObjectInputStream(sock.getInputStream());
                System.out.println("after in e out");
                out.writeObject(r);
                System.out.println("escreveu para Server");
            }
        }
        out.close();
        in.close();
    }

    public void writeData(int[] keys, byte[][] data) throws IOException, ClassNotFoundException {
        this.sock = new Socket(host,port);
        this.out = new ObjectOutputStream(sock.getOutputStream());
        //out.flush();
        this.in = new ObjectInputStream(sock.getInputStream());
        this.request= new Request(keys,data,"put");
        out.writeObject(request);
        Object lido = in.readObject();
        if(lido instanceof Request[]){
            Request[] resp = (Request[])lido;
            for(Request r : resp){
                Socket sock = new Socket(r.getAddress().getHost(),r.getAddress().getPort());
                ObjectOutputStream out= new ObjectOutputStream(sock.getOutputStream());
                ObjectInputStream in = new ObjectInputStream(sock.getInputStream());
                out.writeObject(r);
            }
        }
        in.close();
        out.close();
    }


    public void close(){
        try {
            this.in.close();
            this.out.close();
            this.sock.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public Request esperaRespostas(Request [] requests){
        Thread auxRequests[] = new Thread[requests.length];
        ConcurrentLinkedQueue<Request> queue = new ConcurrentLinkedQueue<Request>();
        int i =0;

        System.out.println("lalala");
        

        for(Request req:requests){
            Socket s = null;

            try {
                s = new Socket(req.getAddress().getHost(),req.getAddress().getPort());
                auxRequests[i]=new Thread(new putHelper(s,req,queue));
                System.out.println("19348");

            } catch (IOException e) {
                e.printStackTrace();
            }
            i++;
        }
        System.out.println("Size "+auxRequests.length);
        for(Thread t: auxRequests){
            t.start();
        }
        for(Thread t:auxRequests){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
                System.out.println("escaxer8");
        Request result = new Request(new HashMap<Integer,byte[]>(),null,"ack");
                System.out.println("escaxer9");
        for(Request r:queue){
                    System.out.println("escaxer10");
            for(Integer in:r.getData().keySet()){
                System.out.println(new String(result.getData().get(r)));
                result.getData().put(in,r.getData().get(in));
            }
            queue.remove();
        }
                System.out.println("YEY");
        return result;
    }

}

